home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mb2curves.py < prev    next >
Text File  |  2004-01-05  |  31KB  |  907 lines

  1. """   QuArK  -  Quake Army Knife Bezier shape makers
  2.  
  3.  
  4. """
  5.  
  6.  
  7. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  8. # FOUND IN FILE "COPYING.TXT"
  9. #
  10.  
  11. ########################################################
  12. #
  13. #                          Curves Plugin
  14. #                          v1.0, May 2000
  15. #                      works with Quark 6.0b2
  16. #
  17. #
  18. #                    by tiglari@hexenworld.com
  19. #
  20. #   You may freely distribute modified & extended versions of
  21. #   this plugin as long as you give due credit to tiglari &
  22. #   Armin Rigo. (It's free software, just like Quark itself.)
  23. #
  24. #   Please notify bugs & improvements to tiglari@hexenworld.com
  25. #
  26. ###
  27. ##########################################################
  28.  
  29. #$Header: /cvsroot/quark/runtime/plugins/mb2curves.py,v 1.30 2002/12/29 04:17:58 tiglari Exp $
  30.  
  31.  
  32. Info = {
  33.    "plug-in":       "Curves plugin",
  34.    "desc":          "Making curves from brushes, etc.",
  35.    "date":          "1 May 2000",
  36.    "author":        "tiglari",
  37.    "author e-mail": "tiglari@hexenworld.com",
  38.    "quark":         "Version 6.0b2" }
  39.  
  40.  
  41. import quarkx
  42. import quarkpy.mapmenus
  43. import quarkpy.mapentities
  44. import quarkpy.mapeditor
  45. import quarkpy.mapcommands
  46. import quarkpy.mapoptions
  47. import quarkpy.maphandles
  48. import quarkpy.dlgclasses
  49. import quarkpy.mapduplicator
  50. StandardDuplicator = quarkpy.mapduplicator.StandardDuplicator
  51. from quarkpy.maputils import *
  52.  
  53. import quarkpy.mapbezier
  54. from quarkpy.b2utils import *
  55. from quarkpy.perspective import *
  56.  
  57. #############################
  58. #
  59. #  MAJOR SECTIONS
  60. #
  61. #  - image builders: implementation of buildimages for the
  62. #      shape-builders
  63. #
  64. #  - duplicators: the duplicator code
  65. #
  66. #  - menus
  67. #
  68. # -- Image builders
  69. #
  70. #    many of these below should probably be stuck inside
  71. #    the appropriate cap/bevel/columnImages method
  72. #
  73.  
  74. def makearchfacecp(bl, tl, tr, br):
  75.     mid = (tl+tr)/2.0
  76.     cp = [[bl, tl, mid, tr, br],
  77.           [(bl+tl)/2.0, tl, mid, tr, (br+tr)/2.0],
  78.           [tl, (tl+mid)/2.0, mid, (mid+tr)/2.0, tr]]
  79.     return cp
  80.  
  81. def makecapfacecp(bl, tl, tr, br):
  82.     bm = (bl+br)/2.0
  83.     tm = (tl+tr)/2.0
  84.     cp = [[bl, (bl+bm)/2.0, bm, (bm+br)/2.0, br],
  85.           [bl, (bl+bm)/2.0, (tm+bm)/2.0, (bm+br)/2.0, br],
  86.           [bl, tl, tm, tr, br]]
  87.     return cp
  88.  
  89.  
  90. def smallerarchbox(box, thick):
  91.     "returns a box for arch, thick smaller than input box"
  92.     fi = thick*(box["brf"]-box["blf"]).normalized
  93.     bi = thick*(box["brb"]-box["blb"]).normalized
  94.     fd = thick*(box["brf"]-box["trf"]).normalized
  95.     bd = thick*(box["brb"]-box["trb"]).normalized
  96.     box2 = {}
  97.     for (corner, delta) in (("blf",fi), ("blb",bi),
  98.             ("tlf",fi+fd), ("tlb",fi+bd), ("trf",fd-fi), ("trb",bd-bi),
  99.             ("brf",-fi), ("brb",-bi)):
  100.         box2[corner]=box[corner]+delta
  101.     return box2
  102.  
  103. def smallerbevelbox(box, thick):
  104.     "returns a box for bevel, thick smaller than input box"
  105.     def gap(goal, source, box=box, thick=thick):
  106.         return thick*(box[goal]-box[source]).normalized
  107.     rf = gap("tlf", "trf")
  108.     rb = gap("tlb", "trb")
  109.     lb = gap("tlf", "tlb")
  110.     zip = quarkx.vect(0,0,0)
  111.     box2 = {}
  112.     for (corner, delta) in (("blf",zip), ("blb",lb),
  113.             ("tlf",zip), ("tlb",lb), ("trf",rf), ("trb",rb+lb),
  114.             ("brf",rf), ("brb",rb+lb)):
  115.         box2[corner]=box[corner]+delta
  116.     return box2
  117.  
  118. def smallercolumnbox(box, thick):
  119.     "returns a box for cp;i,m, thick smaller than input box"
  120.     def gap(goal, source, box=box, thick=thick):
  121.         return thick*(box[goal]-box[source]).normalized
  122.     f = gap("tlf", "trf")
  123.     b = gap("tlb", "trb")
  124.     l = gap("tlf", "tlb")
  125.     r = gap("trf", "trb")
  126.     box2 = {}
  127.     for (corner, delta) in (
  128.           ("tlf",-f-l), ("blf",-f-l),
  129.           ("tlb",-b+l), ("blb",-b+l),
  130.           ("trf",f-r), ("brf",f-r),
  131.           ("trb",b+r), ("brb",b+r)):
  132.         box2[corner]=box[corner]+delta
  133.     return box2
  134.  
  135.  
  136. def archline(pd, a, b, c, d):
  137.     "returns 5-tuple with middle halfway between b and c"
  138.     return [pd[a], pd[b], (pd[b]+pd[c])/2, pd[c], pd[d]]
  139.  
  140.  
  141. def archcurve(pd):
  142.     cp = cpFrom2Rows(archline(pd, "blf", "tlf", "trf", "brf"),
  143.                       archline(pd, "blb", "tlb", "trb", "brb"))
  144.     return cp
  145.  
  146.  
  147. def capImages(o, editor, inverse=0, lower=0, onside=0, open=0, thick=0, faceonly=0,
  148.     stretchtex=0, nofront=0, noback=0, noinner=0, noouter=0, (subdivide,)=1):
  149.   "makes a 'cap' (or arch) on the basis of brush o"
  150.   #
  151.   # Make dictionary of faces u/d/f/b/r/l
  152.   #
  153.   o.rebuildall()
  154.   fdict = faceDict(o)
  155.   if fdict is None:
  156.     return
  157.   #
  158.   # make dictionary of points, 'bottom left front' etc.
  159.   # this one's name is short because we refer to it so often
  160.   #
  161.   if onside:
  162.       fdict = facedict_rflip(facedict_rflip(facedict_rflip(fdict)))
  163.   pd = pointdict(vtxlistdict(fdict,o))
  164.   if lower:
  165.       pd = pointdict_vflip(pd)
  166.       pd = pointdict_hflip(pd)
  167.       texface = fdict["d"].copy()
  168.   else:
  169.       texface = fdict["u"].copy()
  170.   #
  171.   # make the basic inner curved face, a 3x5 quilt
  172.   #
  173.   cp = archcurve(pd)
  174.   #
  175.   # project cps from face to patch (flat projection, distorted)
  176.   #
  177.   cp = texcpFromFace(cp, texface, editor)
  178.   #
  179.   # adjust down sides if wanted
  180.   #
  181.   if not stretchtex:
  182.       if lower:
  183.         right, left = fdict["l"], fdict["r"]
  184.       else:
  185.         right, left = fdict["r"], fdict["l"]
  186.       #
  187.       # For the right and left sides of the guide brush ...
  188.       #
  189.       for side, fulcrum, edge in ((right, "trf", 4), (left, "tlf", 0)):
  190.           #
  191.           #  make a copy of the 'top' (where the texture is being
  192.           #   taken from)
  193.           #
  194.           newside = texface.copy()
  195.           #
  196.           # rotate around the front side corner, so that it is coincident
  197.           #  with the side
  198.           #
  199.           newside.distortion(side.normal, pd[fulcrum])
  200.           #
  201.           # a copy of the patch's control-points with the rotated
  202.           #  face's texture projected onto it (would look good at
  203.           #  one end, terrible at the other)
  204.           #
  205.           cp2 = texcpFromFace(cp, newside, editor)
  206.           #
  207.           # Now set the real patch control points along the edge to
  208.           #  the new ones.
  209.           #
  210.           for index in range(3):
  211.               cp[index][edge]=cp2[index][edge]
  212.   #
  213.   # We have now set the cp's for both edges of the patch correctly,
  214.   #  so we smooth things out
  215.   #
  216.   cp = undistortRows(cp)
  217.   cp = undistortColumns(cp)
  218.   inner = quarkx.newobj('inner:b2')
  219.   cp = subdivideRows(subdivide,cp)
  220.   inner.cp = cp
  221.   inner["tex"] = texface["tex"]
  222.   if thick:
  223.       pd2 = smallerarchbox(pd, thick)
  224.       cp2 = archcurve(pd2)
  225.       cp2 = subdivideRows(subdivide,cp2)
  226.       inner.shortname = "outer"
  227.       inner2=quarkx.newobj("inner:b2")
  228.       cp2 = texcpFromCp(cp2, cp)
  229.       inner2.cp = cp2
  230.       inner2["tex"] = inner["tex"]
  231.       #
  232.       # seams
  233.       #
  234.       seams = []
  235.       if not open:
  236.           if not nofront:
  237.               fseam = b2From2Rows(archline(pd, "brf", "trf", "tlf", "blf"),
  238.                            archline(pd2,"brf", "trf", "tlf", "blf"),
  239.                           fdict["f"], "front")
  240.               fseam.cp = subdivideRows(subdivide,fseam.cp)
  241.               seams.append(fseam)
  242.           if not noback:
  243.               bseam = b2From2Rows(archline(pd, "blb", "tlb", "trb", "brb"),
  244.                            archline(pd2,"blb", "tlb", "trb", "brb"),
  245.                            fdict["b"], "back")
  246.               bseam.cp = subdivideRows(subdivide,bseam.cp)
  247.               seams.append(bseam)
  248.       if lower:
  249.         inner.swapsides()
  250.         bseam.swapsides()
  251.         fseam.swapsides()
  252.       else:
  253.         inner2.swapsides()
  254.       inners = []
  255.       if not faceonly:
  256.           if not noouter:
  257.               inners.append(inner)
  258.           if not noinner:
  259.               inners.append(inner2)
  260.       return inners + seams
  261.   # end if thick
  262.  
  263. #  if lower:
  264.  #     inner.swapsides()
  265.   if inverse:
  266.      inner.swapsides()
  267.   if open:
  268.       return [inner]
  269.   if inverse:
  270.      fcp = makearchfacecp(pd["blf"],pd["tlf"],pd["trf"],pd["brf"])
  271.      bcp = makearchfacecp(pd["blb"],pd["tlb"],pd["trb"],pd["brb"])
  272.   else:
  273.      fcp = makecapfacecp(pd["blf"],pd["tlf"],pd["trf"],pd["brf"])
  274.      bcp = makecapfacecp(pd["blb"],pd["tlb"],pd["trb"],pd["brb"])
  275. #  if lower:
  276. #      fcp = transposeCp(fcp)
  277. #  else:
  278.   if subdivide>1:
  279.       if not nofront:
  280.           fcp = subdivideRows(subdivide, fcp)
  281.       if not noback:
  282.           bcp = subdivideRows(subdivide, bcp)
  283.   bcp = transposeCp(bcp)
  284.   faces = []
  285.   if not nofront:
  286.       front = b2FromCpFace(fcp, 'front', fdict["f"], editor)
  287.       faces.append(front)
  288.   if not noback:
  289.       back = b2FromCpFace(bcp,'back', fdict["b"], editor)
  290.       faces.append(back)
  291.   if faceonly:
  292.       return faces
  293.   return [inner]+faces
  294.  
  295.  
  296. def bevelImages(o, editor, inverse=0, lower=0, left=0, standup=0, open=0, thick=0,
  297.   faceonly=0, stretchtex=0, notop=0, nobottom=0, noinner=0, noouter=0, (subdivide,)=1):
  298.   "makes a bevel/inverse bevel on the basis of brush o"
  299.   o.rebuildall()
  300.   #
  301.   # make a dictionary where faces are indexed by the first letter
  302.   # of their name (front|back|left|right|up|down)
  303.   #
  304.   fdict = faceDict(o)
  305.   if fdict is None:
  306.     return
  307.   if standup:
  308.       fdict = facedict_rflip(fdict)
  309.   if lower:
  310.       fdict = facedict_fflip(fdict)
  311.   #
  312.   # get a dict of the vertices indexed by [t|b][r|l][f|b]
  313.   #
  314.   pd = pointdict(vtxlistdict(fdict,o))
  315.   #
  316.   # interchange left and right vertices for left bevel
  317.   #
  318.   if left:
  319.       pd = pointdict_hflip(pd)
  320.   #
  321.   # make patch controlpoints, rows starting at the back, curving
  322.   #  towards front (for regular and left versions).  For left bevels,
  323.   #  'l' vertices will be right and vice versa
  324.   #
  325.   def bevelcurve(pd):
  326. #      return cpFrom2Rows(subdivideLine(2, pd["tlb"], pd["trb"],pd["trf"]),
  327. #                         subdivideLine(2, pd["blb"],pd["brb"],pd["brf"]))
  328.       return cpFrom2Rows([pd["tlb"], pd["trb"],pd["trf"]],
  329.                          [pd["blb"],pd["brb"],pd["brf"]])
  330.   cp = bevelcurve(pd)
  331.   inner = quarkx.newobj('inner:b2')
  332.   #
  333.   # project the texture from the back face onto the patch
  334.   #
  335.   cp = texcpFromFace(cp, fdict["b"], editor)
  336.   if not stretchtex:
  337.       newside = fdict["b"].copy()
  338.       #
  339.       # fdict has not been flipped for left brushes
  340.       #
  341.       if left:
  342.           newside.distortion(fdict["l"].normal,pd["trb"])
  343.       else:
  344.           newside.distortion(fdict["r"].normal,pd["trb"])
  345.       cp2 = texcpFromFace(cp, newside, editor)
  346.       debug("cp: ")
  347.       writecps(cp)
  348. #      debug("cp2: ")
  349. #      writecps(cp2)
  350.       for index in range(3):
  351.         cp[index][2]=cp2[index][2]
  352.  
  353.   cp = undistortRows(cp)
  354.   cp = undistortColumns(cp)
  355.   cp = subdivideRows(subdivide,cp)
  356.   inner.cp = cp
  357.   inner["tex"] = fdict["b"]["tex"]
  358.   if thick:
  359.       inner.shortname="outer"
  360.       pd2 = smallerbevelbox(pd, thick)
  361.       cp2 = bevelcurve(pd2)
  362.       inner2=quarkx.newobj("inner:b2")
  363.       inner2.cp = texcpFromCp(subdivideRows(subdivide,cp2), cp)
  364.       inner2["tex"]=inner["tex"]
  365.       tseam = b2From2Rows([pd["trf"], pd["trb"], pd["tlb"]],
  366.                        [pd2["trf"], pd2["trb"], pd2["tlb"]],
  367.                         fdict["u"],"top")
  368.       bseam = b2From2Rows([pd["blb"], pd["brb"], pd["brf"]],
  369.                        [pd2["blb"], pd2["brb"], pd2["brf"]],
  370.                         fdict["d"],"bottom")
  371.       tseam.cp = subdivideRows(subdivide, tseam.cp)
  372.       bseam.cp = subdivideRows(subdivide, bseam.cp)
  373.       if left:
  374.           inner.swapsides()
  375.           tseam.swapsides()
  376.           bseam.swapsides()
  377.       else:
  378.           inner2.swapsides()
  379.       if lower:
  380.           inner.swapsides()
  381.           inner2.swapsides()
  382.           tseam.swapsides()
  383.           bseam.swapsides()
  384.       seams = [tseam, bseam]
  385.       if notop:
  386.           seams.remove(tseam)
  387.       if nobottom:
  388.           seams.remove(bseam)
  389.       if faceonly:
  390.          return seams
  391.       inners = [inner, inner2]
  392.       if noinner:
  393.           inners.remove(inner2)
  394.       if noouter:
  395.           inners.remove(inner)
  396.       return inners+seams
  397.   if left:
  398.     inner.swapsides()
  399.   if inverse:
  400.     inner.swapsides()
  401.   if lower:
  402.      inner.swapsides()
  403.   if open:
  404.       return [inner]
  405.   if inverse:
  406.       tcp = cpFrom2Rows([pd["tlb"], pd["trb"], pd["trf"]],
  407.                          [pd["trb"], pd["trb"], pd["trf"]])
  408.       bcp = cpFrom2Rows([pd["brf"], pd["brb"], pd["blb"]],
  409.                          [pd["brf"], pd["brb"], pd["brb"]])
  410.   else:
  411.       tcp = subdivideRows(subdivide,cpFrom2Rows([pd["trf"], pd["trb"], pd["tlb"]],
  412.                          [pd["tlf"], pd["tlf"], pd["tlb"]]))
  413.       bcp = subdivideRows(subdivide,cpFrom2Rows([pd["blb"], pd["brb"], pd["brf"]],
  414.                          [pd["blb"], pd["blf"], pd["blf"]]))
  415.   top = b2FromCpFace(tcp,"top",fdict["u"],editor)
  416.   bottom = b2FromCpFace(bcp,"bottom",fdict["d"],editor)
  417.   if left:
  418.     top.swapsides()
  419.     bottom.swapsides()
  420.   if lower:
  421.     top.swapsides()
  422.     bottom.swapsides()
  423.   faces = [bottom, top]
  424.   if notop:
  425.       faces.remove(top)
  426.   if nobottom:
  427.       faces.remove(bottom)
  428.   if faceonly:
  429.     return faces
  430.   return [inner] + faces
  431.  
  432.  
  433. def circleLine(p0, p1, p2, p3):
  434.     return [(p0+p1)/2, p1, (p1+p2)/2, p2, (p2+p3)/2, p3,
  435.             (p3+p0)/2, p0, (p0+p1)/2]
  436.  
  437. def columnImages(o, editor, inverse=0, open=0, thick=0, stretchtex=0, bulge=(.5,1),
  438.       funnel=None, faceonly=0, notop=0, nobottom=0, noinner=0, noouter=0, circle=0, (subdivide,)=1):
  439.     "makes a column on the basis of brush o"
  440.     if circle:
  441.         subfunc=arcSubdivideLine
  442.     else:
  443.         subfunc=None
  444.     o.rebuildall()
  445.     fdict = faceDict(o)
  446.     if fdict is None:
  447.         return
  448.     pdo = pointdict(vtxlistdict(fdict,o))
  449.  
  450.     if funnel is not None:
  451.  
  452.         def warpbox(pd, pdo=pdo,funnel=funnel):
  453.             pd2 = {}
  454.             for (i, p0, p1, p2, p3) in ((0, "tlf", "tlb", "trb", "trf"),
  455.                                         (1, "blf", "blb", "brb", "brf")):
  456.                 c = (pd[p0]+pd[p1]+pd[p2]+pd[p3])/4.0
  457.                 for p in (p0, p1, p2, p3):
  458.                     pd2[p] = c+funnel[i]*(pd[p]-c)
  459.             return pd2
  460.  
  461.         pd = warpbox(pdo)
  462.     else:
  463.         pd = pdo
  464.  
  465.  
  466.     def curveCp(pd, bulge=bulge):
  467.         cp = cpFrom2Rows(circleLine(pd["trf"], pd["tlf"], pd["tlb"], pd["trb"]),
  468.                         circleLine(pd["brf"], pd["blf"], pd["blb"], pd["brb"]),bulge)
  469.         return cp
  470.  
  471.     cp = curveCp(pd)
  472.  
  473.     def makeTube (cp, pd, oldface, pdo=pdo, fdict=fdict, stretchtex=stretchtex, subfunc=subfunc, subdivide=subdivide,editor=editor):
  474.         cp2 = interpolateGrid(pdo["tlb"], pdo["trb"], pdo["blb"], pdo["brb"], 3, 9)
  475.         cp2 = texcpFromFace(cp2, oldface, editor)
  476.         cp = texcpFromCp(cp, cp2)
  477.         if subdivide>1:
  478.             cp = subdivideRows(subdivide,cp,subfunc)
  479.         if not stretchtex:
  480.             for (facekey, corner) in (("r", "trb"),("f", "trf"),("l","tlf")):
  481.                 newface=oldface.copy()
  482.                 newface.setthreepoints(oldface.threepoints(2),2)
  483.                 newface.distortion(fdict[facekey].normal,pdo[corner])
  484.                 oldface = newface
  485.             cp3 = interpolateGrid(pdo["tlf"], pdo["tlb"], pdo["blf"], pdo["blb"])
  486.             cp3 = texcpFromFace(cp3, oldface, editor)
  487.             for i in range(3):
  488.                 cp[i][8] = quarkx.vect(cp[i][8].xyz+cp3[i][2].st)
  489. #    squawk(`cp`)
  490.         cp = undistortRows(cp)
  491.         inner = quarkx.newobj("inner:b2")
  492.         inner.cp = cp
  493.         inner["tex"]=oldface["tex"]
  494.         return inner
  495.  
  496.     inner = makeTube(cp, pd, fdict["b"])
  497.     if thick:
  498.         pd2 = smallercolumnbox(pd, thick)
  499.         cp2 = curveCp(pd2)
  500.         inner2 = makeTube(cp2, pd2, fdict["f"])
  501.         inner2.shortname="inner"
  502.         inner.shortname="outer"
  503.         tcp = cpFrom2Rows(cp[0],cp2[0])
  504.         bcp = cpFrom2Rows(cp[2], cp2[2])
  505.         if subdivide>1:
  506.             tcp = subdivideRows(subdivide,tcp, subfunc)
  507.             bcp = subdivideRows(subdivide,bcp, subfunc)
  508.         top = b2FromCpFace(tcp,"top",fdict["u"],editor)
  509.         top.swapsides()
  510.         bottom = b2FromCpFace(bcp,"bottom",fdict["d"],editor)
  511.         inner2.swapsides()
  512.         seams = [top, bottom]
  513.         if notop:
  514.             seams.remove(top)
  515.         if nobottom:
  516.             seams.remove(bottom)
  517.         if faceonly:
  518.            return seams
  519.         inners = [inner, inner2]
  520.         if noinner:
  521.             inners.remove(inner2)
  522.         if noouter:
  523.             inners.remove(inner)
  524.         return inners+seams
  525.  
  526.     if open:
  527.        if inverse:
  528.           inner.swapsides()
  529.        return [inner]
  530.  
  531.  
  532.     if inverse:
  533.  
  534.         def squareFromCircle(row): # row = 9 pts, cp's for circle
  535.             # first not used, passed to reduce index confusion
  536.             def halfSquare(hr): # hr=half-row excluding center
  537.                 return [hr[1], hr[1], hr[2], hr[3], hr[3]]
  538.  
  539.             return halfSquare(row[:4]), halfSquare(row[4:8])
  540.  
  541.         def faces(circline, borderfunc, name, texface, subdivide=subdivide,subfunc=subfunc):
  542.             out0, out1 = borderfunc(circline)
  543.             b2a = b2From2Rows(out0, circline[0:5],texface,name+'0',subdivide=subdivide,subfunc=subfunc)
  544.             b2b = b2From2Rows(out1, circline[4:9],texface,name+'1',subdivide=subdivide,subfunc=subfunc)
  545.             return b2a, b2b
  546.  
  547.         topa, topb = faces(cp[0],squareFromCircle,'top',fdict['u'])
  548.         inner.swapsides()
  549.         topa.swapsides()
  550.         topb.swapsides()
  551.         bottoma, bottomb = faces(cp[2],squareFromCircle,'bottom',fdict['d'])
  552.         result = [inner, topa, topb, bottoma, bottomb]
  553.         if faceonly:
  554.             result.remove(inner)
  555.         if notop:
  556.             result.remove(topa)
  557.             result.remove(topb)
  558.         if nobottom:
  559.             result.remove(bottoma)
  560.             result.remove(bottomb)
  561.     else:
  562.         def center(v):
  563.             c = (v[1]+v[3]+v[5]+v[7])/4.0
  564.             return map(lambda x,c=c:c,range(9))
  565.  
  566.         top = b2From2Rows(center(cp[0]),cp[0],fdict['u'],'top',subdivide=subdivide,subfunc=subfunc)
  567.         bottom = b2From2Rows(cp[2], center(cp[2]),fdict['d'],'bottom',subdivide=subdivide,subfunc=subfunc)
  568.         result = [inner, top, bottom]
  569.         if faceonly:
  570.             result.remove(inner)
  571.         if notop:
  572.             result.remove(top)
  573.         if nobottom:
  574.             result.remove(bottom)
  575.  
  576.     return result
  577.  
  578.  
  579. def images(buildfn, args):
  580.     if quarkx.setupsubset(SS_MAP, "Options")["Developer"]:
  581.         return apply(buildfn, args)
  582.     else:
  583.         try:
  584.             return apply(buildfn, args)
  585.         except:
  586.             return []
  587.  
  588. #
  589. #  --- Duplicators ---
  590. #
  591.  
  592. class CapDuplicator(StandardDuplicator):
  593.  
  594.   def buildimages(self, singleimage=None):
  595.     if singleimage is not None and singleimage>0:
  596.       return []
  597.     editor = mapeditor()
  598.     inverse, lower, onside, open, thick, faceonly, stretchtex, nofront, noback, noinner, noouter, subdivide = map(lambda spec,self=self:self.dup[spec],
  599.       ("inverse", "lower", "onside", "open", "thick", "faceonly", "stretchtex",
  600.          "nofront", "noback", "noinner", "noouter", "subdivide"))
  601.     if thick:
  602.       thick, = thick
  603.     if subdivide is None:
  604.         subdivide=1,
  605.     list = self.sourcelist()
  606.     for o in list:
  607.       if o.type==":p": # just grab the first one, who cares
  608.          return images(capImages, (o, editor, inverse, lower, onside, open, thick,
  609.            faceonly, stretchtex, nofront, noback, noinner, noouter, subdivide))
  610.  
  611.  
  612. class BevelDuplicator(StandardDuplicator):
  613.  
  614.   def buildimages(self, singleimage=None):
  615.     if singleimage is not None and singleimage>0:
  616.       return []
  617.     editor = mapeditor()
  618.     inverse, lower, left, standup, sidetex, open, thick, faceonly, stretchtex, notop, nobottom, noinner, noouter, subdivide = map(lambda spec,self=self:self.dup[spec],
  619.       ("inverse", "lower", "left", "standup", "sidetex", "open", "thick", "faceonly", "stretchtex",
  620.          "notop", "nobottom", "noinner", "noouter", "subdivide"))
  621.     if thick:
  622.       thick, = thick
  623.     list = self.sourcelist()
  624.     if subdivide is None:
  625.         subdivide=1,
  626.     for o in list:
  627.       if o.type==":p": # just grab the first one, who cares
  628.            return images(bevelImages, (o, editor, inverse, lower, left, standup, open, thick,
  629.               faceonly, stretchtex, notop, nobottom, noinner, noouter, subdivide))
  630.  
  631. class ColumnDuplicator(StandardDuplicator):
  632.  
  633.   def buildimages(self, singleimage=None):
  634.     if singleimage is not None and singleimage>0:
  635.       return []
  636.     editor = mapeditor()
  637.     inverse, open, thick, stretchtex, bulge, funnel, faceonly,notop,nobottom, noinner, noouter, circle, subdivide = map(lambda spec,self=self:self.dup[spec],
  638.       ("inverse", "open", "thick", "stretchtex", "bulge", "funnel", "faceonly", "notop", "nobottom", "noinner","noouter",  "circle", "subdivide"))
  639.     if thick:
  640.       thick, = thick
  641.     list = self.sourcelist()
  642.     if subdivide is None:
  643.         subdivide=1,
  644.     for o in list:
  645.       if o.type==":p": # just grab the first one, who cares
  646.            return images(columnImages, (o, editor, inverse, open, thick, stretchtex, bulge,funnel,
  647.              faceonly,notop,nobottom, noinner, noouter, circle,subdivide))
  648.  
  649. quarkpy.mapduplicator.DupCodes.update({
  650.   "dup cap":     CapDuplicator,
  651.   "dup bevel":   BevelDuplicator,
  652.   "dup column":  ColumnDuplicator
  653. })
  654.  
  655. #
  656. #  --- Menus ---
  657. #
  658.  
  659. def curvemenu(o, editor, view):
  660.  
  661.   def makecap(m, o=o, editor=editor):
  662.       dup = quarkx.newobj(m.mapname+":d")
  663.       dup["macro"]="dup cap"
  664.       if m.inverse:
  665.         dup["inverse"]=1
  666.       dup.appenditem(m.newpoly)
  667.       undo=quarkx.action()
  668.       undo.exchange(o, dup)
  669.       if m.inverse:
  670.         editor.ok(undo, "make arch")
  671.       else:
  672.         editor.ok(undo, "make cap")
  673.       editor.invalidateviews()
  674.  
  675.  
  676.   def makebevel(m, o=o, editor=editor):
  677.       dup = quarkx.newobj("bevel:d")
  678.       dup["macro"]="dup bevel"
  679.       dup["inverse"]=1
  680.       if m.left:
  681.         dup["left"]=1
  682.       dup["open"]=1  # since this is normally rounding a corner with wall & ceiling"
  683.       dup.appenditem(m.newpoly)
  684.       undo=quarkx.action()
  685.       undo.exchange(o, dup)
  686.       if m.left:
  687.         editor.ok(undo, "make left corner")
  688.       else:
  689.         editor.ok(undo, "make right corner")
  690.  
  691.   def makecolumn(m, o=o, editor=editor):
  692.       dup = quarkx.newobj("column:d")
  693.       dup["macro"]="dup column"
  694.       dup["open"]=1  # since this is normally rounding a corner with wall & ceiling"
  695.       dup.appenditem(m.newpoly)
  696.       undo=quarkx.action()
  697.       undo.exchange(o, dup)
  698.       editor.ok(undo, "make column")
  699.  
  700.   disable = (len(o.subitems)!=6)
  701.  
  702.   newpoly = perspectiveRename(o, view)
  703.   list = []
  704.  
  705.   def finishitem(item, disable=disable, o=o, view=view, newpoly=newpoly):
  706.       disablehint = "This item is disabled because the brush doesn't have 6 faces."
  707.       if disable:
  708.           item.state=qmenu.disabled
  709.           try:
  710.               item.hint=item.hint + "\n\n" + disablehint
  711.           except (AttributeError):
  712.               item.hint="|" + disablehint
  713.       else:
  714.           item.o=o
  715.           item.newpoly = newpoly
  716.           item.view = view
  717.  
  718.   for (menname, mapname, inv) in (("&Arch", "arch",  1), ("&Cap", "cap", 0)):
  719.     item = qmenu.item(menname, makecap)
  720.     item.inverse = inv
  721.     item.mapname = mapname
  722.     finishitem(item)
  723.     list.append(item)
  724.  
  725.   cornerhint = """|Makes a smooth curve from the %s side of the brush to the back.
  726.  
  727. The texture is taken from the back wall, and sized across the curve (compressed a bit) to align with this texture as wrapped onto the %s wall.
  728.  
  729. To make a rounded corner, put a brush into a corner, project the texture of one of the room walls onto the paralell & `kissing' face of the brush, arrange the camera/view so you're looking square on at the brush and this face is the back one, then and RMB|Curves|right/left corner depending on whether the room-wall you're next to is to the right or the left.
  730.  
  731. If the textures on the two adjoining walls of the room are properly aligned, the texture on the curve will be too (compressed a bit, but not so as to make much of a difference).
  732. """
  733.  
  734.  
  735.   for (name, left, hint) in (("&Left corner", 1, cornerhint%("left","left")),
  736.                        ("&Right corner", 0, cornerhint%("right","right"))):
  737.     item = qmenu.item(name, makebevel)
  738.     item.inverse = 1
  739.     item.left = left
  740.     item.hint = hint
  741.     finishitem(item)
  742.     list.append(item)
  743.  
  744.   colitem = qmenu.item("C&olumn", makecolumn, "Make a column")
  745.   finishitem(colitem)
  746.   list.append(colitem)
  747.  
  748.   curvehint = """|Commands for making curves out of brushes.
  749.  
  750. The brush must be roughly a box, with the usual six sides.  The curve is implemented as a `duplicator' containing the brush, which determines the overall shape of the brush.
  751.  
  752. To resize the curve, select the brush in the treeview, and manipulate the sides in the usual manner (when the brush itself is selected, the curve becomes invisible).
  753.  
  754. When the duplicator is selected, the entity page provides a variety of specifics that can be manipulated to convert from an `arch' to a `cap' (by unchecking 'inverse'), and much else besides.
  755.  
  756. The curve will be oriented w.r.t. the map view you RMB-clicked on, or, if you're RMB-ing on the treeview, the most recent mapview you clicked in.
  757.  
  758. If the brush vanishes without being replaced by a shape, the brush may have been too screwy a shape, or looked at from a bad angle. (My attempts to detect these conditions in advance are meeting with unexpected resistance. There is also a bug in that if you apply this to a brush after first opening the map editor, without inserting anything first, the orientations are wrong.)
  759. """
  760.   curvepop = qmenu.popup("Curves",list, hint=curvehint)
  761.   if newpoly is None:
  762.     if len(o.subitems)!=6:
  763.       morehint= "\n\nThis item is disabled because the poly doesn't have exactly 6 faces."
  764.     else:
  765.       morehint="\n\nThis item is disabled because I can't figure out which face is front, back, etc.  Make it more box-like, and look at it more head-on in the view."
  766.     curvepop.hint = curvepop.hint+morehint
  767.     curvepop.state = qmenu.disabled
  768.   return curvepop
  769.  
  770. #
  771. # First new menus are defined, then swapped in for the old ones.
  772. #  `im_func' returns from a method a function that can be
  773. #   assigned as a value.
  774. #
  775. def newpolymenu(o, editor, oldmenu=quarkpy.mapentities.PolyhedronType.menu.im_func):
  776.     "the new right-mouse perspective menu for polys"
  777.     #
  778.     # cf FIXME in maphandles.CenterHandle.menu
  779.     #
  780.  
  781.     beziersupport = quarkx.setupsubset()["BezierPatchSupport"]
  782.     if (beziersupport is not None) and (beziersupport == "1"):
  783.         # only allow the curves-submenu, if the game-mode supports bezierpatches
  784.         try:
  785.             view = editor.layout.clickedview
  786.         except:
  787.             view = None
  788.         return  [curvemenu(o, editor, view)]+oldmenu(o, editor)
  789.     else:
  790.         return  oldmenu(o, editor)
  791.  
  792. #
  793. # This trick of redefining things in modules you're based
  794. #  on and importing things from is something you couldn't
  795. #  even think about doing in C++...
  796. #
  797. # It's actually warned against in the Python programming books
  798. #  -- can produce hard-to-understand code -- but can do cool
  799. #  stuff.
  800. #
  801. #
  802. quarkpy.mapentities.PolyhedronType.menu = newpolymenu
  803.  
  804.  
  805. # ----------- REVISION HISTORY ------------
  806. #$Log: mb2curves.py,v $
  807. #Revision 1.30  2002/12/29 04:17:58  tiglari
  808. #transfer fixes from 6.3
  809. #
  810. #Revision 1.29.14.2  2002/12/29 02:59:28  tiglari
  811. #a bit of cleanup, remove failed attempt to supress centering tex coordinates
  812. # with threepoints call; this now down in quarkpy/bqurils:texcpfromface
  813. #
  814. #Revision 1.29.14.1  2002/12/28 23:52:18  tiglari
  815. #add _fixed flag to inhibit L-square recentering of tex alignment faces
  816. #
  817. #Revision 1.29  2001/03/01 19:14:16  decker_dk
  818. #changed newpolymenu() so it checks 'BezierPatchSupport' to see if it should allow the Curves-menu.
  819. #
  820. #Revision 1.28  2001/02/25 04:46:49  tiglari
  821. #new specifics for brush&patch arch&bevel
  822. #
  823. #Revision 1.27  2001/02/14 10:08:58  tiglari
  824. #extract perspective stuff to quarkpy.perspective.py
  825. #
  826. #Revision 1.26  2000/09/04 21:29:03  tiglari
  827. #added lots of specifics to column generator, fixed column & arch bugs
  828. #
  829. #Revision 1.25  2000/09/02 11:25:43  tiglari
  830. #added subdivides & detail specifics to arch/cap.  last two (ends/sides) are howeer still unimplemented
  831. #
  832. #Revision 1.24  2000/07/26 11:37:31  tiglari
  833. #thick arch/bevel bugz fixed
  834. #
  835. #Revision 1.23  2000/06/30 11:01:06  tiglari
  836. #fixed thick bevel bug
  837. #
  838. #Revision 1.22  2000/06/26 22:54:58  tiglari
  839. #renaming: antidistort_rows/columns->undistortRows/Colunmns,
  840. #tanaxes->tanAxes, copy/map/transposecp->copy/map/transposeCP
  841. #
  842. #Revision 1.21  2000/06/25 23:47:01  tiglari
  843. #Function Renaming & Reorganization, hope no breakage
  844. #
  845. #Revision 1.19  2000/06/25 11:30:11  tiglari
  846. #oops, bugfix for cones & bulges for columns, some function renaming
  847. #
  848. #Revision 1.18  2000/06/25 11:02:23  tiglari
  849. #cones & bulges for columns, some function renaming
  850. #
  851. #Revision 1.17  2000/06/25 06:09:34  tiglari
  852. #top & bottom plates for columns, some texturing bugfixes
  853. #
  854. #Revision 1.16  2000/06/24 09:40:17  tiglari
  855. #thickness for columns
  856. #
  857. #Revision 1.15  2000/06/22 22:39:40  tiglari
  858. #added support for columns (and pipes)
  859. #
  860. #Revision 1.14  2000/06/19 11:46:22  tiglari
  861. #Fixes to texture alignment on arches
  862. #
  863. #Revision 1.13  2000/06/17 09:42:53  tiglari
  864. #yet another texture scale fix (upper arches de-borked again...
  865. #
  866. #Revision 1.12  2000/06/17 07:35:12  tiglari
  867. #arch/cap texture now projected off top or bottom for normal
  868. #and lower, respectively; stretchtex option added vs. complex
  869. #alignment.
  870. #
  871. #Revision 1.11  2000/06/16 10:48:26  tiglari
  872. #Fixed perspective-driven builder problems
  873. #
  874. #Revision 1.10  2000/06/16 06:02:42  tiglari
  875. #fixed coordinate handedness screwup in floating map veiews
  876. #
  877. #Revision 1.9  2000/06/16 05:11:31  tiglari
  878. #fixed antidistortion on arch underside, which got broken
  879. #
  880. #Revision 1.8  2000/06/14 04:41:18  tiglari
  881. #Added `faceonly' specific for arches & bevels.
  882. #
  883. #Revision 1.7  2000/06/13 12:52:40  tiglari
  884. #Supported all the current arch/cap and bevel specifics
  885. #
  886. #Revision 1.6  2000/06/12 11:18:20  tiglari
  887. #Added bevel duplicator and round corner curves submenu items for Q3
  888. #
  889. #Revision 1.5  2000/06/04 03:23:50  tiglari
  890. #reduced/eliminated distortion on arch/cap curve face
  891. #
  892. #Revision 1.4  2000/06/03 13:01:25  tiglari
  893. #fixed arch duplicator maploading problem, hopefully, also
  894. #arch duplicator map writing problem
  895. #
  896. #Revision 1.3  2000/06/03 10:25:30  alexander
  897. #added cvs headers
  898. #
  899. #Revision 1.2  2000/05/28 06:28:48  tiglari
  900. #fixed problem with revision history (2 of them, no# for first snap)
  901. #
  902. #Revision 1.1  2000/05/26 23:04:17  tiglari
  903. #Arch-builders.  There's a bug in quark.clickform, which doesn't seem to work right until something has been dropped into the map.
  904. #
  905. #
  906.  
  907.